/*
 * Author:   Sander
 * Date:     19-mei-2011
 * Name:     Mesh
 * Version:  1.0
 *
 * This code is property of Sander blahblahblah copyright blahblah.
 */
package javaraytracer.Objects;

import java.util.ArrayList;
import javaraytracer.IntersectionInfo;
import javaraytracer.Materials.Material;
import javaraytracer.Ray;
import javaraytracer.Vec3;

/**
 * Mesh object, use for multiple meshes in a model. Not for single/manual use!
 * @author Sander
 */
public class Mesh extends Traceable
{
	public Triangle[] polygons = null;
	public String name = "unknown mesh";
	public Sphere bounding;
	
	public Mesh(Triangle[] pol, String name)
	{
		polygons = pol;
		this.name = name;
		
		trim();
		material = polygons[0].material;
		
		Initialize();
	}
	
	private void Initialize()
	{
		// bounding sphere/box
		double minX = Double.MAX_VALUE;
		double maxX = Double.MIN_VALUE;
		double minY = Double.MAX_VALUE;
		double maxY = Double.MIN_VALUE;
		double minZ = Double.MAX_VALUE;
		double maxZ = Double.MIN_VALUE;
		for(int k = 0; k < polygons.length; k++)
		{
			for(int l = 0; l < 3; l++)
			{
				Vec3 p = polygons[k].getVector(l);
				if(p.x < minX) minX = p.x;
				if(p.x > maxX) maxX = p.x;
				if(p.y < minY) minY = p.y;
				if(p.y > maxY) maxY = p.y;
				if(p.z < minZ) minZ = p.z;
				if(p.z > maxZ) maxZ = p.z;
			}
			
		}

		Vec3 origin = new Vec3((minX + maxX) / 2.0, (minY + maxY) / 2.0, (minZ + maxZ) / 2.0);
		float radius = (float)(new Vec3(maxX, maxY, maxZ).sub(origin)).length();

		bounding = new Sphere(origin, radius, Material.WHITE);
	}
	
	private void trim()
	{
		ArrayList<Triangle> list = new ArrayList<Triangle>();
		for(int i = 0; i < polygons.length; i++)
		{
			if(polygons[i] != null)
			{
				list.add(polygons[i]);
			}
		}
		polygons = null;
		polygons = list.toArray(new Triangle[list.size()]);
		list = null;
	}
	
	public void setParent(Traceable object)
	{
		this.parent = object;
		for(int k = 0; k < polygons.length; k++)
		{
			polygons[k].parent = this.parent;
		}
	}


	@Override
	public IntersectionInfo intersect(Ray r)
	{
		//return bounding.intersect(r);
		
		IntersectionInfo nearestHit = null;

		if(bounding.intersect(r).hit)
		{
			for(int i = 0; i < polygons.length; i++)
			{
				
				IntersectionInfo ii = polygons[i].intersect(r);
				if(ii.hit && (nearestHit == null || ii.distance < nearestHit.distance))
				{
					nearestHit = ii;
				}
				
			}
			if(nearestHit != null)
			{
				return nearestHit;
			}
		}
		
		return new IntersectionInfo(false);
	}


	@Override
	public boolean hit(Ray r)
	{
		if(bounding.hit(r))
		{
			for(int i = 0; i < polygons.length; i++)
			{
				if(polygons[i].hit(r))
				{
					return true;
				}
			}
		}
		return false;
	}


	@Override
	public Material getMaterial()
	{
		return material;
	}


	@Override
	public Material getMaterial(IntersectionInfo info)
	{
		return material;
	}
	
}
